package org.metasketch.editor.toolbox.advanced;

import groovy.lang.Binding;
import groovy.lang.GroovyShell;
import javafx.geometry.Point2D;
import javafx.scene.input.MouseButton;
import javafx.scene.input.MouseEvent;
import javafx.scene.shape.Line;
import org.metasketch.diagram.javafx.graphics.BaseGraphic;
import org.metasketch.diagram.javafx.graphics.BaseLink;
import org.metasketch.editor.links.DefaultLinkHandler;
import org.metasketch.editor.links.GraphicalLink;
import org.metasketch.editor.links.ILink;
import org.metasketch.editor.links.LinkEndCalculator;
import org.metasketch.editor.links.LinkEndOrientation;
import org.metasketch.editor.timeline.SnapshotManager;
import org.metasketch.editor.toolbox.DefaultToolHandler;
import org.metasketch.editor.toolbox.IToolHandler;
import org.metasketch.editor.toolbox.SimpleToolBaseDiagramType;
import org.metasketch.editor.toolbox.advanced.ISemanticTypeTester;
import org.metasketch.graphics.Point;
import org.metasketch.javafx.graphics.JFXDiagram;
import org.metasketch.model.BaseModel;
import org.metasketch.model.CommonIdentified;

public class DefaultCalloutHandler implements IToolHandler {

	Line shadowLine = new Line();

	// String startGraphicType = null;
	BaseGraphic startObject = null;

	private boolean shadowLink = false;

	private BaseGraphic endObject;

	String graphicTypeName;

	String nodeGraphicName;

	String linkGraphicName;

	Class nodeSemanticType;
	Class linkSemanticType;

	protected Point2D dragAnchor;

	protected double initX;
	protected double initY;

	private JFXDiagram diagram;

	private SimpleToolBaseDiagramType diagramType;

	private boolean useSourceSemantic;

	// String semanticTestScript;

	public DefaultCalloutHandler(String graphicTypeName,
			SimpleToolBaseDiagramType diagramType, boolean useSourceSemantic) {
		this.graphicTypeName = graphicTypeName;

		shadowLine.setTranslateX(0);
		shadowLine.setTranslateY(0);
		shadowLine
				.setStyle("-fx-stroke: gray; -fx-stroke-width: 3;   -fx-stroke-dash-array: 12 2 4 2;    -fx-stroke-dash-offset: 6;     -fx-stroke-line-cap: butt; -fx-opacity: 0.5;");

		this.diagramType = diagramType;

		this.useSourceSemantic = useSourceSemantic;

	}

	public DefaultCalloutHandler(String nodeGraphicBaseName,
			String linkGraphicName, SimpleToolBaseDiagramType diagramType) {
		this.nodeGraphicName = nodeGraphicBaseName;
		this.linkGraphicName = linkGraphicName;
		this.diagramType = diagramType;
	}

	public DefaultCalloutHandler(Class nodeType, Class linkType,
			SimpleToolBaseDiagramType diagramType) {
		this.nodeSemanticType = nodeType;
		this.linkSemanticType = linkType;
		this.diagramType = diagramType;
	}

	@Override
	public void handle(MouseEvent event, JFXDiagram jfxDiagram, BaseModel model) {
		try {

			Object source = event.getSource();

			// String graphicType;
			if (event.getEventType() == MouseEvent.MOUSE_CLICKED
					&& event.getButton() == MouseButton.PRIMARY) {

				if (!shadowLink) {

					boolean possible = false;

					// clicked on an element
					if (source instanceof BaseGraphic) {
						BaseGraphic baseGraphic = (BaseGraphic) source;
						// String graphicType = baseGraphic.getGraphicNode()
						// .getType();

						possible = true;
						startObject = baseGraphic;
					}

					if (possible) {

						activateLink(event, jfxDiagram);
					}

				} else {
					applyShadowLink(event, jfxDiagram, model);
				}

				event.consume();

			} else if (event.getEventType() == MouseEvent.MOUSE_MOVED) {

				if (shadowLink)
					updateShadowLine(event);

				event.consume();

			}

		} catch (Throwable e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		}

	}

	public void setInitDragParams(MouseEvent event) {
		if (event.getSource() instanceof BaseGraphic) {
			BaseGraphic baseGraphic = (BaseGraphic) event.getSource();
			// initX = baseGraphic.getTranslateX();
			// initY = baseGraphic.getTranslateY();
			dragAnchor = new Point2D(event.getSceneX(), event.getSceneY());

			Point completePosition = baseGraphic
					.getCompleteCoordinatesThroughParent();

			initX = completePosition.getX() + event.getX();
			initY = completePosition.getY() + event.getY();
		} else {
			dragAnchor = new Point2D(event.getSceneX(), event.getSceneY());

			initX = event.getX();
			initY = event.getY();
		}

	}

	private void activateLink(MouseEvent event, JFXDiagram jfxDiagram) {
		shadowLink = true;

		this.diagram = jfxDiagram;

		setInitDragParams(event);

		double x = event.getX();
		double y = event.getY();

		if (startObject != null) {

			setShadowLineStartPosition(x, y);

			x += startObject.getTranslateX();
			y += startObject.getTranslateY();

		} else {
			shadowLine.setStartX(x);
			shadowLine.setStartY(y);
		}

		shadowLine.setEndX(x);
		shadowLine.setEndY(y);

		jfxDiagram.addBlindNode(shadowLine);

	}

	protected void setShadowLineStartPosition(double x, double y) {

		// double x = event.getX();
		// double y = event.getY();

		if (startObject != null) {

			// if (initialEndConfig == null) {
			//
			// x += startObject.getTranslateX();
			// y += startObject.getTranslateY();
			//
			// } else {

			Point point = LinkEndCalculator.getCoordinates(x, y, startObject,
					LinkEndOrientation.CLOSEST);
			x = point.getX();
			y = point.getY();

			// }

			shadowLine.setStartX(x);
			shadowLine.setStartY(y);
		}

		System.out.println("start :" + x + ", " + y);

	}

	private void updateShadowLine(MouseEvent event) {

		double x = event.getSceneX() - dragAnchor.getX() + initX;
		double y = event.getSceneY() - dragAnchor.getY() + initY;

		setShadowLineStartPosition(x, y);

		// System.out.println(event);

		Point mousePoint = LinkEndCalculator.getCoordinates(
				shadowLine.getStartX(), shadowLine.getStartY(), x - 1, y - 1,
				2, 2, LinkEndOrientation.CLOSEST);

		shadowLine.setEndX(mousePoint.getX());
		shadowLine.setEndY(mousePoint.getY());

		System.out.println("end " + mousePoint.getX() + ", "
				+ mousePoint.getY());

	}

	private void applyShadowLink(MouseEvent event, JFXDiagram jfxDiagram,
			BaseModel model) throws Throwable {

		cleanup();

		Object source = event.getSource();

		// boolean possible = false;

		// todo REPLACE BY PROPER CALLOUT CREATION...
		// buildLink(jfxDiagram, model, endType);

		createCallout(event, jfxDiagram, model, startObject.getSemantic());

	}

	private void createCallout(MouseEvent event, JFXDiagram jfxDiagram,
			BaseModel model, CommonIdentified semantic) {
		try {
			IToolHandler nodeTool = null;
			IToolHandler linkTool = null;

			if (graphicTypeName != null && nodeGraphicName == null
					&& nodeSemanticType == null) {

				nodeTool = diagramType.getToolHandlerRegistry().get(
						graphicTypeName + "-Node");
			} else if (nodeGraphicName != null && nodeSemanticType == null) {
				nodeTool = diagramType.getToolHandlerRegistry().get(
						nodeGraphicName);
			} else if (nodeSemanticType != null) {
				nodeTool = diagramType.getToolHandlerRegistry().get(
						nodeSemanticType);
			}

			if (graphicTypeName != null && linkGraphicName == null
					&& linkSemanticType == null) {
				linkTool = diagramType.getToolHandlerRegistry().get(
						graphicTypeName + "-Link");
			} else if (linkGraphicName != null && linkSemanticType == null) {
				linkTool = diagramType.getToolHandlerRegistry().get(
						linkGraphicName);
			} else if (linkSemanticType != null) {
				linkTool = diagramType.getToolHandlerRegistry().get(
						linkSemanticType);
			}

			if (nodeTool == null || linkTool == null)
				return;

			if (linkTool instanceof DefaultLinkHandler) {
				DefaultLinkHandler dtool = (DefaultLinkHandler) linkTool;
				// TODO HORRIBLE HAAACKCKKK
				// only allow valid links...
				ILink link = dtool.getLink();
				if (link instanceof GraphicalLink) {
					if (link.getEndType(startObject.getGraphicNode().getType(),
							nodeGraphicName) == null)
						return;
				}

			}

			BaseGraphic calloutNode = null;

			if (nodeTool instanceof DefaultToolHandler) {
				DefaultToolHandler dtool = (DefaultToolHandler) nodeTool;

				if (useSourceSemantic)
					calloutNode = (BaseGraphic) dtool.treatCreationEvent(event,
							jfxDiagram, model, null, semantic, false);
				else
					calloutNode = (BaseGraphic) dtool.treatCreationEvent(event,
							jfxDiagram, model, null, null, false);

			}

			if (linkTool instanceof DefaultLinkHandler) {
				DefaultLinkHandler dtool = (DefaultLinkHandler) linkTool;

				if (useSourceSemantic)
					dtool.buildLink(startObject, calloutNode, jfxDiagram,
							model, semantic, false);
				else
					dtool.buildLink(startObject, calloutNode, jfxDiagram,
							model, null, false);
			}

			SnapshotManager.takeSnapshot();

		} catch (Throwable e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		}

	}

	@Override
	public void cleanup() {

		if (shadowLink) {
			diagram.removeNode(shadowLine);
			shadowLink = false;

			diagram = null;
		}

	}

	public String getNodeGraphicName() {
		return nodeGraphicName;
	}

	public void setNodeGraphicName(String nodeGraphicName) {
		this.nodeGraphicName = nodeGraphicName;
	}

	public String getLinkGraphicName() {
		return linkGraphicName;
	}

	public void setLinkGraphicName(String linkGraphicName) {
		this.linkGraphicName = linkGraphicName;
	}

	public Class getNodeSemanticType() {
		return nodeSemanticType;
	}

	public void setNodeSemanticType(Class nodeSemanticType) {
		this.nodeSemanticType = nodeSemanticType;
	}

	public Class getLinkSemanticType() {
		return linkSemanticType;
	}

	public void setLinkSemanticType(Class linkSemanticType) {
		this.linkSemanticType = linkSemanticType;
	}

	public boolean isUseSourceSemantic() {
		return useSourceSemantic;
	}

	public void setUseSourceSemantic(boolean useSourceSemantic) {
		this.useSourceSemantic = useSourceSemantic;
	}

}
